package contact

import (
	"center/db"
	"common/uuid"
	"common/ws_contact"
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/zeebo/errs"
	"go.uber.org/zap"
	"golang.org/x/sync/errgroup"
	"log"
	"net/http"
	"time"
)

var Error = errs.Class("Contact")

type Config struct {
	Address     string        `help:"心跳地址" releaseDefault:":10002" default:":10002"`
	OfflineTime time.Duration `help:"离线判定时间" releaseDefault:"30m" default:"10m"`
	TlsPemFile  string        `help:"tls证书文件"  default:"$ROOT/estore.guanjieai.com.pem"`
	TlsKeyFile  string        `help:"tls证书文件"  default:"$ROOT/estore.guanjieai.com.key"`
}

type Server struct {
	log      *zap.Logger
	config   *Config
	endpoint ws_contact.ContactCenter
	server   http.Server
	db       *db.CenterDB
	hub      *ws_contact.Hub
	//nodeClient *NodeClient
}

func NewContactServer(log *zap.Logger, cfg Config, end ws_contact.ContactCenter, db *db.CenterDB) *Server {
	server := &Server{
		log:      log,
		config:   &cfg,
		endpoint: end,
		db:       db,
		hub:      ws_contact.NewHub(),
	}
	root := mux.NewRouter()
	root.HandleFunc("/heartbeat", server.heartbeat)
	server.server.Handler = root
	return server
}

func (s *Server) heartbeat(w http.ResponseWriter, r *http.Request) {
	var err error = nil
	nodeId := r.Header.Get("Sec-WebSocket-Protocol")
	fmt.Println("r.Header:", r.Header)
	if nodeId == "" {
		w.WriteHeader(401)
		err = errors.New("Sec-WebSocket-Protocol nodeId empty")
		fmt.Println(err)
		return
	}
	ws_contact.ServeWs(s.hub, s, nodeId, w, r)
}

func (s *Server) Run(ctx context.Context) error {
	cert, err := tls.LoadX509KeyPair(s.config.TlsPemFile, s.config.TlsKeyFile)
	if err != nil {
		log.Println(err)
		return err
	}
	tlsConfig := &tls.Config{Certificates: []tls.Certificate{cert}}
	listener, err := tls.Listen("tcp", s.config.Address, tlsConfig)
	if err != nil {
		return err
	}
	ctx, cancel := context.WithCancel(ctx)
	var group errgroup.Group
	group.Go(func() error {
		<-ctx.Done()
		return Error.Wrap(s.server.Shutdown(context.Background()))
	})
	group.Go(func() error {
		defer cancel()
		err := s.server.Serve(listener)
		if errors.Is(err, http.ErrServerClosed) {
			err = nil
		}
		return Error.Wrap(err)
	})
	group.Go(func() error {
		return s.checkOffline(ctx)
	})
	group.Go(func() error {
		s.hub.Run(ctx)
		return nil
	})
	go func() {
		for {
			t1 := time.NewTicker(time.Second * 2)
			select {
			case <-t1.C:
				//id, _ := uuid.FromString("9edcec34-963b-43b8-8c32-d05b23f39438")
				//req := ws_contact.PieceDeleteRequest{
				//	Key: "abcdd",
				//}
				//fmt.Println("PieceDelete", s.PieceDelete(id, &req))
			}
		}
	}()

	s.log.Info("contact start: " + s.config.Address)
	return group.Wait()
}

func (s *Server) checkOffline(ctx context.Context) error {
	if s.config.OfflineTime == 0 {
		s.log.Error("离线判定时间为空, 未启动离线监控")
		return nil
	}
	s.db.CheckOffline(ctx, s.config.OfflineTime)
	tk := time.NewTicker(s.config.OfflineTime)
	defer tk.Stop()
	for {
		select {
		case <-ctx.Done():
			return nil
		case <-tk.C:
			_ = s.db.CheckOffline(ctx, s.config.OfflineTime)
		}
	}
}

// DealReqMsg 消息分发
func (s *Server) DealReqMsg(msg *ws_contact.Package) (data any, err error) {
	ctx := context.Background()
	switch msg.Action {
	case ws_contact.CheckIn:
		var param ws_contact.CheckInRequest
		if err = msg.DecData(&param); err != nil {
			return
		}
		return s.endpoint.CheckIn(ctx, param)
	case ws_contact.Report:
		var param ws_contact.ReportRequest
		if err = msg.DecData(&param); err != nil {
			return
		}
		err = s.endpoint.Report(ctx, param)
		return nil, err
	}
	return nil, nil
}

func (s *Server) Ping(nodeId uuid.UUID) error {
	return nil
}

func (s *Server) PieceDelete(nodeId uuid.UUID, req *ws_contact.PieceDeleteRequest) error {
	data, err := ws_contact.PackMsgDelRequest(req)
	if err != nil {
		return err
	}
	return s.hub.Write(nodeId.String(), data)
}
